from kivy.logger import Logger

from irc.client import NickMask, ip_quad_to_numstr

__all__ = ['DCCHandShakeModel']

class DCCHandShakeModel(object):
    """Initiate a handshake with self._appdata['client'].send_dcc_chat_request(self._connection, self._target)
    IRCClientModel will let this handle the handshakes. See IRCClientModel.on_dccmsg
    """
    def __init__(self, appdata):
        self._appdata = appdata
        self._appdata['client'].register_on_ctcp_dcc_chat_invite(self.handle_on_ctcp_dcc_chat_invite)
        self._appdata['client'].register_on_ctcp_dcc_chat_handshake(self.handle_on_ctcp_dcc_chat_handshake)
        self._connectors = {}
        
    def _find_my_nick(self, server):
        record = self._appdata['servers'].get_server(server)
        if record is not None:
            return record['nick']
        else:
            return None

    def handle_on_ctcp_dcc_chat_invite(self, connection, source, peer_address, peer_port):
        # received an invite through the server
        nmask = NickMask(source)
        nick = nmask.nick
        record = self._appdata['trusted_users'].get_user(nick, connection.server)
        if record is not None:
            rules = self._appdata['rules'].get(record['rule_name'])
            # create a new handshake handler
            my_nick = self._find_my_nick(connection.server)
            if my_nick is not None:
                handler = _DCCHandShakeModel(self, my_nick, nick, connection.server, rules, _DCCHandShakeModel.invitee, self._appdata['client'])
                self._connectors[peer_address] = handler
                if not handler.reply_to_invite(peer_address, peer_port):
                    # end handshaking
                    self.release_child(handler, False)

    def handle_on_ctcp_dcc_chat_handshake(self, dcc_connection, nick, server, handshake):
        # receive a handshake peer to peer
        peeraddress = dcc_connection.peeraddress
        if peeraddress in self._connectors:
            # the peer started its dcc server
            #   and sent the invite through the server
            #   and this app has already sent the first handshake
            #   and is now receiving the second handshake
            # process this handshake
            handler = self._connectors[peeraddress]
            handler.handshake(handshake)
        else:
            # this app started the dcc server
            #  then sent the invite through the server
            #  and is now receiving this first handshake from the peer
            # so create a handler
            record = self._appdata['trusted_users'].get_user(nick, server)
            if record is not None:
                rules = self._appdata['rules'].get(record['rule_name'])
            my_nick = self._find_my_nick(server)
            if my_nick is not None:
                handler = _DCCHandShakeModel(self,
                                             my_nick,
                                             nick,
                                             server,
                                             rules,
                                             _DCCHandShakeModel.invitor,
                                             self._appdata['client'])
                handler.dcc_connection = dcc_connection
                self._connectors[peeraddress] = handler
                handler.handshake(handshake)

    def release_child(self, child, continue_connection):
        # release the child
        if child.dcc_connection is not None:
            peeraddress = child.dcc_connection.peeraddress
            if continue_connection:
                # open the view
                self._appdata['connection_vm'].add_dcc_connection(child.dcc_connection,
                                                                  child.target_nick,
                                                                  child.server)
            else:
                # don't continue the connection
                child.dcc_connection.disconnect()
            # remove the child
            del(self._connectors[peeraddress])

    def _warn_user(self):
        self._appdata['connection_vm'].view.message_warn('The DCC connection attempt with "%s" failed.')


class _DCCHandShakeModel(object):

    invitor = 1
    invitee = 2

    def __init__(self, parent, my_nick, nick, server, rules, state, irc_client):
        self._parent = parent
        self._my_nick = my_nick
        self.target_nick = nick # target
        self.server = server
        self._irc_client = irc_client
        self._rules = rules
        self._server_connection = None
        self.dcc_connection = None
        self.state = state

    def invite(self, server_connection, nick):
        self._server_connection = server_connection
        # send the ctcp message
        try:
          self._irc_client.send_dcc_chat_request(server_connection,
                                                 self.target_nick)
          # waiting for initial handshake
          # the command is handshake
          self._irc_client.register_on_ctcp_dcc_chat_accept(self.handle_ctcp_chat_accept)
        except Exception as e:
          pass
          
    def _build_handshake_message(self, handshake):
          return '%s %s handshake %s' % (self._my_nick, self.server, handshake)

    def reply_to_invite(self, peer_address, peer_port):
        # the invite came through the server
        try:
            self.dcc_connection = self._irc_client.dcc_connect(peer_address, peer_port)
            # return a handshake
            handshake = self._rules.apply_handshake_send_rules(self._rules.handshake)
            message = self._build_handshake_message(handshake[0])
            self.dcc_connection.privmsg(message)
            return True
        except Exception as e:
            # end the handshaking
            return False

    def handshake(self, text):
        ok = self._is_valid_handshake(text)
        if ok:
            # valid handshake with trusted user
            if self.state == self.__class__.invitor:
                # this initiated the connection with the invite
                # the peer sent a handshake
                # return a handshake
                # handshaking is over
                handshake = self._rules.apply_handshake_send_rules(self._rules.handshake)
                message = self._build_handshake_message(handshake[0])
                try:
                  self.dcc_connection.privmsg(message)
                except Exception as e:
                  ok = False
            else:
                # the peer initaited the connection
                # this app sent the first handshake
                # this handshake if the final one
                # handshaking is over
                pass
        else:
            pass
        # end the handshaking
        self._parent.release_child(self, ok)
            
    def _is_valid_handshake(self, text):
        clean = self._rules.apply_handshake_receive_rules(text)
        return clean == self._rules.handshake
        
